

function patchProps(el, oldData = {}, data = {}) {
    //  老的属性有 新的属性无则删除
    let oldStyles = oldData.style || {}
    let newStyles = data.style || {}

    for (let key in oldStyles) { // 如果有老的style  新的没有则删除 
        if (!newStyles[key]) {
            el.style[key] = ''
        }
    }

    for (let key in oldData) {// 如果有老的属性 新的没有则删除 
        if (!data[key]) {
            el.removeAttribute(key)
        }
    }


    for (let key in data) { // 用新的属性覆盖旧的属性 或style
        if (key === 'style') {
            for (let styleName in data.style) {
                el.style[styleName] = data.style[styleName]
            }

        } else {
            el.setAttribute(key, data[key])
        }
    }
}
function createComponent(vNode) {
    let i = vNode.data
    if ((i = i.hook) && (i = i.init)) {
        i(vNode)
    }
    if (vNode.componentInstance) {
        return true
    }
}

export function createElement(vNode) { // 创建真是dom
    const { tagName, data, text, children } = vNode
    if (typeof tagName === 'string') {

        if (createComponent(vNode)) { // 组件
            return vNode.componentInstance.$el
        }


        vNode.el = document.createElement(tagName)
        patchProps(vNode.el, {}, data)
        children && children.forEach(element => {
            vNode.el.appendChild(createElement(element))
        });
    } else {
        vNode.el = document.createTextNode(text)
    }
    return vNode.el

}

export function patch(oldVNode, vNode) { // 将vNode转为真DOM

    if (!oldVNode) { // 组件的挂载
        return createElement(vNode)
    }

    const isDom = oldVNode.nodeType // 判断是否为真是dom
    if (isDom) {
        let ele = oldVNode
        let parentEle = ele.parentNode; // 找到父节点
        let newEle = createElement(vNode)

        parentEle.insertBefore(newEle, ele.nextSibling) //插入到老ele的下面
        parentEle.removeChild(ele) // 删除原来的

        return newEle
    } else {
        // diff
        // 策略  1元素不同不对比  2相同节点比较属性   3节点比较完成比较儿子
        return patchVNode(oldVNode, vNode)
    }
}

function patchVNode(oldVNode, vNode) {
    if (!isSameVNode(oldVNode, vNode)) { // 不是相同节点
        let el = createElement(vNode)
        oldVNode.el.parentNode.replaceChild(createElement(vNode), oldVNode.el)
        return el
    }
    let el = vNode.el = oldVNode.el
    if (!oldVNode.tagName) { // 是否为文本
        if (oldVNode.text !== vNode.text) { //将新文本替换旧文本
            el.textContent = vNode.text
        }
    }

    patchProps(el, oldVNode.data, vNode.data) // 比较属性

    // 比较子节点    1 一方有一方无  2  两都有
    let oldChildren = oldVNode.children || []
    let newChildren = vNode.children || []

    if (oldChildren.length > 0 && newChildren.length > 0) {
        updateChildren(el, oldChildren, newChildren)
    } else if (newChildren.length > 0) {
        mountChildren(el, newChildren)

    } else if (oldChildren.length > 0) {
        unMountChildren(el,)
    }


    return el
}
function unMountChildren(el,) { // 删除新节点中没有得到节点
    el.innerHTML = ''
}


function mountChildren(el, newChildren) { // 挂载新加的子节点
    for (let i = 0; i < newChildren.length; i++) {
        let child = newChildren[i]
        el.appendChild(createElement(child))
    }
}


function isSameVNode(vNode1, vNode2) { // 是否为相同节点 
    return vNode1.tagName === vNode2.tagName && vNode1.key === vNode2.key
}


function updateChildren(el, oldChildren, newChildren) {

    let map = {}

    oldChildren.forEach((v, i) => {
        if (!map[v.key]) {
            map[v.key] = i
        }
    })

    // 使用双指针的方式 比较两个节点
    let oldStartIndex = 0
    let newStartIndex = 0
    let oldEndIndex = oldChildren.length - 1
    let newEndIndex = newChildren.length - 1

    let oldStartVNode = oldChildren[0]
    let newStartVNode = newChildren[0]

    let oldEndVNode = oldChildren[oldEndIndex]
    let newEndVNode = newChildren[newEndIndex]

    while (oldStartIndex <= 0 && newStartIndex <= 0) {
        if (!oldStartVNode) { // 乱序时被处理为undefined时
            oldStartVNode = oldChildren[++oldStartIndex]
        } else if (!oldEndVNode) {
            oldEndVNode = oldChildren[--oldEndIndex]
        } else if (isSameVNode(oldStartVNode, newStartVNode)) { // 新老为同一节点  前前
            patchVNode(oldStartVNode, newStartVNode) // 深度优先 递归比较
            oldStartVNode = oldChildren[++oldStartIndex]
            newStartVNode = newChildren[++newStartIndex]
            continue
        } else if (isSameVNode(oldEndVNode, newEndVNode)) { // 新老为同一节点  后后
            patchVNode(oldEndVNode, newEndVNode) // 深度优先 递归比较
            oldEndVNode = oldChildren[--oldEndIndex]
            newEndVNode = newChildren[--newEndIndex]
            continue
        } else if (isSameVNode(oldEndVNode, newStartVNode)) { // 新老为同一节点 后前
            patchVNode(oldEndVNode, newStartVNode) // 深度优先 递归比较
            el.insertBefore(oldEndVNode.el, oldStartVNode.el) // 满足条件时将 最后一个移动道最前面

            oldEndVNode = oldChildren[--oldEndIndex]
            newStartVNode = newChildren[++newStartIndex]
            continue
            // 将
        } else if (isSameVNode(oldStartVNode, newEndVNode)) { // 新老为同一节点  后后
            patchVNode(oldStartVNode, newEndVNode) // 深度优先 递归比较
            // 如果匹配  则将第一个插入到最后一个的后一个的前面  相当于查到最后
            el.insertBefore(oldStartVNode.el, oldEndVNode.el.nextSibling())
            oldStartVNode = oldChildren[++oldStartIndex]
            newEndVNode = newChildren[--newEndIndex]
            continue
        }
        // 乱序  制作映射表 用新几点去找 有则取值 无责添加  余则删除
        let moveIndex = map[newStartVNode.key]
        if (moveIndex !== undefined) {
            let moveVNode = oldChildren[moveIndex] // 需移动的节点
            el.insertBefore(moveVNode.el, oldStartVNode.el)
            oldChildren[moveIndex] = 0
            patchProps(moveVNode, newStartVNode) //移动完毕 对比属性

        } else {
            el.insertBefore(createElement(newStartVNode), oldStartVNode.el)
        }
        newStartVNode = newChildren[++newStartIndex]

    }
    if (newStartIndex <= newEndIndex) { // 循环结束 新的还有剩余  需要添加
        for (let i = newStartIndex; i <= newEndIndex; i++) {
            let childEle = createElement(newChildren[i])
            let anchor = newChildren[newEndIndex + 1] ? newChildren[newEndIndex + 1] : null
            el.insertBefore(childEle, anchor)// 当后一个元素为null时相当于 appendChild
        }
    }
    if (oldStartIndex <= oldEndIndex) { // 循环结束 老的还有剩余  需要删除
        for (let i = oldStartIndex; i <= oldEndIndex; i++) {
            if (oldChildren[i]) {
                let childEle = oldChildren[i].el
                el.removeChild(childEle)
            }
        }
    }
}